/*************************************************************************
	> File Name: ./src/PageCache.cpp
	> Author: zq
	> Mail: zhouquan1511@163.com 
	> Created Time: Thu 27 Apr 2023 10:55:02 PM CST
 ************************************************************************/
#include "PageCache.h"

PageCache PageCache::_sInst;

//获取一个k页Span
//pageCache按照页数直接映射，最大为128页Span 
Span* PageCache::NewSpan(size_t k) {
	assert(k > 0);

	//大于k>128，则直接向系统申请
	if (k > NPAGES - 1) {
		void *ptr = SystemAlloc(k);

		Span *span = _spanPool.New();
		span->_pageId = (PAGE_ID)ptr >> PAGE_SHIFT;
		span->_n = k;
		_idSpanMap[span->_pageId] = span; //建立映射，用于该块内存的回收

		return span;
	}

	//1 检查k号桶，有Span直接返回
	if (!_spanLists[k].Empty()) {
		Span *kSpan = _spanLists[k].PopFront();

		/*注意，停留在pageCache的Span处于游离状态
		未来可能就被切分成多个Span了，因此我们只针
		对这些返回给centralCache的Span建立确定的映射
		留在pageCache的Span是具有不确定性的*/
		for (PAGE_ID i = 0; i < kSpan->_n; i++) {
			_idSpanMap[kSpan->_pageId + i] = kSpan;
		}
		return kSpan;
	}

	//2 向后遍历检查k+1~128号桶，直到找到一个n页Span，找到了就切分为n/n-k页Span
	for (int i = k + 1; i < NPAGES; i ++) {
		if (!_spanLists[i].Empty()) {
			Span *nSpan = _spanLists[i].PopFront();
			Span *kSpan = _spanPool.New();
			kSpan->_pageId = nSpan->_pageId;
			kSpan->_n = k;
			nSpan->_pageId += k;
			nSpan->_n -= k;
			_spanLists[nSpan->_n].PushFront(nSpan);

			//用于centralCache->pageCache回收内存Span
			_idSpanMap[nSpan->_pageId] = nSpan;
			_idSpanMap[nSpan->_pageId + nSpan->_n - 1] = nSpan;

			//用于threadCache->centralCache回收内存obj
			for (PAGE_ID i = 0; i < kSpan->_n; i++) {
				_idSpanMap[kSpan->_pageId + i] = kSpan;
			}

			return kSpan;
		}
	}

	//3 没找到就向系统申请内存，大小为128页，递归调用NewSpan
	Span *bigSpan = _spanPool.New();
	void *ptr = SystemAlloc(NPAGES - 1);
	bigSpan->_pageId = (PAGE_ID)ptr >> PAGE_SHIFT;
	bigSpan->_n = NPAGES - 1;
	_spanLists[bigSpan->_n].PushFront(bigSpan);
	return NewSpan(k);
}

Span* PageCache::MapObjectToSpan(void *obj) {
	PAGE_ID id = (PAGE_ID)obj >> PAGE_SHIFT;

	std::unique_lock<std::mutex> lock(_pageMtx); //构造时加锁，析构时自动解锁
	auto ret = _idSpanMap.find(id);
	if (ret != _idSpanMap.end()) {
		return ret->second;
	}
	else {
		assert(false);
		return nullptr;
	}
}

Span* PageCache::ReleaseSpanToPageCache(Span *span) {
	//如果待回收span对应的页数大于128页，直接由系统回收
	if (span->_n > NPAGES - 1) {
		void *ptr = (void*)(span->_pageId << PAGE_SHIFT);
		size_t size = span->_n << PAGE_SHIFT;
		SystemFree(ptr, size);
		_spanPool.Delete(span);

		return nullptr;
	}

	//1 向前合并
	while (1) {
		PAGE_ID prevId = span->_pageId - 1;
		auto ret = _idSpanMap.find(prevId);
		if (ret == _idSpanMap.end()) {
			break;
		}

		Span *prevSpan = ret->second;
		if (prevSpan->_isUse == true) {
			break;
		}

		if (prevSpan->_n + span->_n > NPAGES - 1) {
			break;
		}

		span->_pageId = prevSpan->_pageId;
		span->_n += prevSpan->_n;
		_spanLists[prevSpan->_n].Erase(prevSpan);
		_spanPool.Delete(prevSpan);
	}

	//2 向后合并
	while (1) {
		PAGE_ID nextId = span->_pageId + span->_n;
		ret = _idSpanMap(nextId);
		
		if (ret == _idSpanMap.end()) {
			break;
		}

		Span *nextSpan = ret->second;
		if (nextSpan->_isUse == true) {
			break;
		}

		if (nextSpan->_n + span->_n > NPAGES - 1) {
			break;
		}

		span->_n += nextSpan->_n;
		
		_spanLists[nextSpan->_n].Erase(nextSpan);
		_spanPool.Delete(nextSpan);
	}
	_spanLists[span->_n].PushFront(span);
	_idSpanMap[span->_pageId] = span;
	_idSpanMap[span->_pageId + span->_n - 1] = span;
	span->_isUse = false;
}
